/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package recommender.bayesian;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import recommender.objects.Entity;
import recommender.objects.Video;
import smile.Network;

/**
 * A class to read a knowledge base network
 * @author Aya
 */
public class BayesianReader {
    
    private final Network bnet;
    
    
    public BayesianReader(String path, String fname, int alg){
        bnet = new Network();
        bnet.readFile(path+"/"+fname);
        bnet.setBayesianAlgorithm(alg);
    }
    
    public Network getNetwork(){
        return bnet;
    }

    public void setEvidence(List<Video> watched, List<Video> disliked, List<Entity> interests, List<Entity> dislikes) {
        bnet.clearAllEvidence();
        for(Video video: watched){
            bnet.setEvidence(video.getIdentifier(), 0);
        }
        
        for(Video video: disliked){
            bnet.setEvidence(video.getIdentifier(), 1);
        }
        
        for(Entity entity: interests){
            bnet.setEvidence(entity.getIdentifier(), 0);
        }
        
        for(Entity entity: dislikes){
            bnet.setEvidence(entity.getIdentifier(), 1);
        }
        bnet.updateBeliefs();
    }
    
    public void setEvidence(List<Integer> favorites){
        bnet.clearAllEvidence();
        for(int fav: favorites){
            bnet.setEvidence("Video"+fav, 0);
        }
        bnet.updateBeliefs();
    }

    public Map<String, Double> getImplicitInterests(int n) {
        List<String> interests = new ArrayList<>();
        double[] probabilities = new double[n];
        int min_index = 0;
        
        String[] nodes = bnet.getAllNodeIds();
        for(String node: nodes){
            if(!node.startsWith("Video")){
                double prob = bnet.getNodeValue(node)[0];
                if(prob != 1){
                    if(interests.size() <  n){
                        interests.add(node);
                        probabilities[interests.size()-1] = prob;
                        min_index = getMin(probabilities);
                    }
                    else{
                        if(prob > probabilities[min_index]){
                            interests.set(min_index, node);
                            probabilities[min_index] = prob;
                            min_index = getMin(probabilities);
                        }
                    }
                }
            }
        }
        
        Map<String, Double> map = new HashMap<>();
        for(int i=0; i<n; i++)
            if(probabilities[i] > 0)
                map.put(interests.get(i), probabilities[i]);
        return map;
    }
    
    public int getMin(double[] arr){
        int min = 0;
        double minval = 1.0;
        for(int i=0; i<arr.length; i++){
            if(arr[i]<minval){
                minval = arr[i];
                min = i;
            }
        }
        return min;
    }

    public Map<String, Double> getRecommendations(int n) {
        List<String> recommendations = new ArrayList<>();
        double[] probabilities = new double[n];
        int min_index = 0;
        String[] nodes = bnet.getAllNodeIds();
        for(String node: nodes){
            if(node.startsWith("Video")){
                double prob = bnet.getNodeValue(node)[0];
                if(prob != 1.0){
                    if(recommendations.size() < n){
                    recommendations.add(node);
                    probabilities[recommendations.size()-1] = prob;
                    min_index = getMin(probabilities);
                    }    
                    else {
                        if(prob > probabilities[min_index]){
                            recommendations.set(min_index, node);
                            probabilities[min_index] = prob;
                            min_index = getMin(probabilities);
                        }
                    }
                }
            }
        }
        Map<String, Double> map = new HashMap<>();
        for(int i=0; i<n; i++)
            if(probabilities[i] > 0)
                map.put(recommendations.get(i), probabilities[i]);
        return map;
    }
    
    public Map<String, Double> getRecommendationsByThreshold(int th) {
        double t = th*0.01;
        Map<String, Double> map = new HashMap<>();
        String[] nodes = bnet.getAllNodeIds();
        for(String node: nodes){
            if(node.startsWith("Video")){
                double prob = bnet.getNodeValue(node)[0];
                if(prob != 1 && prob > t){
                    map.put(node, prob);
                }
            }
        }
        return map;
    }
    
    public Map<String, Double> getImplicitInterestsByThreshold(int th) {
        double t = th*0.01;
        Map<String, Double> map = new HashMap<>();
        String[] nodes = bnet.getAllNodeIds();
        for(String node: nodes){
            if(!node.startsWith("Video")){
                double prob = bnet.getNodeValue(node)[0];
                if(prob != 1 && prob > t){
                    map.put(node, prob);
                }
            }
        }
        return map;
    }

    public void setGroupEvidence(int i, List<Video> watched, List<Video> disliked, List<Entity> interests, List<Entity> dislikes) {
        bnet.clearAllEvidence();
        for(Video video: watched){
            bnet.setEvidence(video.getIdentifier()+"_u"+i, 0);
        }
        
        for(Video video: disliked){
            bnet.setEvidence(video.getIdentifier()+"_u"+i, 1);
        }
        
        for(Entity entity: interests){
            bnet.setEvidence(entity.getIdentifier()+"_u"+i, 0);
        }
        
        for(Entity entity: dislikes){
            bnet.setEvidence(entity.getIdentifier()+"_u"+i, 1);
        }
        bnet.updateBeliefs();
    }
    
    public Map<String, Double> getGroupRecommendations(int n) {
        List<String> recommendations = new ArrayList<>();
        double[] probabilities = new double[n];
        int min_index = 0;
        
        String[] nodes = bnet.getAllNodeIds();
        for(String node: nodes){
            if(node.startsWith("Group_Video")){
                double prob = bnet.getNodeValue(node)[0];
                if(prob != 1.0){
                    if(recommendations.size() < n){
                    recommendations.add(node);
                    probabilities[recommendations.size()-1] = prob;
                    min_index = getMin(probabilities);
                    }    
                    else {
                        if(prob > probabilities[min_index]){
                            recommendations.set(min_index, node);
                            probabilities[min_index] = prob;
                            min_index = getMin(probabilities);
                        }
                    }
                }
            }
        }
        Map<String, Double> map = new HashMap<>();
        for(int i=0; i<n; i++)
            if(probabilities[i] > 0)
                map.put(recommendations.get(i).replace("Group_", ""), probabilities[i]);
        return map;
    }
}
